#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <openssl/aes.h>


#define PRE_KEY "AES: "


/** 引入nginx三个头文件 **/


/*处理配置的七个回调*/
static ngx_int_t   preconfiguration(ngx_conf_t *cf);
static ngx_int_t    postconfiguration(ngx_conf_t *cf);
//static void       *create_main_conf(ngx_conf_t *cf);
//static char       *init_main_conf(ngx_conf_t *cf, void *conf);
//static void       *create_srv_conf(ngx_conf_t *cf);
//static char       *merge_srv_conf(ngx_conf_t *cf, void *prev, void *conf);
static void       *create_loc_conf(ngx_conf_t *cf);
//static  char       *merge_loc_conf(ngx_conf_t *cf, void *prev, void *conf);

/*处理指令 bingo_config*/
static char *ngx_conf_set_bingo_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_conf_set_bingo_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

/*读取文件内容*/
static ngx_str_t  *ngx_read_file_content(ngx_conf_t *cf, ngx_str_t *path);
static u_char  *ngx_decode_aes_ceb(ngx_conf_t *cf,u_char *in, u_char *key);
 

ngx_http_module_t ngx_http_bingo_config_module_ctx =
{
    preconfiguration,      /*解析配置文件前调用*/
    postconfiguration,     /*完成配置文件的解析后调用*/
    NULL,   /*当需要创建数据结构用于存储main级别（直属于http{...}块的配置项）的全局配置项时，可以通过create_main_conf回调方法创建存储全局配置项的结构体*/
    NULL, /*常用于初始化main级别配置项*/
    NULL,/*当需要创建数据结构用于存储srv级别（直属于虚拟主机server{...}块的配置项）的配置项时，可以通过实现create_srv_conf回调方法创建存储srv级别配置项的结构体*/
    NULL, //merge_srv_conf回调方法主要用于合并main级别和srv级别下的同名配置项
    create_loc_conf, /*当需要创建数据结构用于存储loc级别（直属于location{...}块的配置项）的配置项时，可以实现create_loc_conf回调方法*/
    NULL     // merge_loc_conf回调方法主要用于合并srv级别和loc级别下的同名配置项
};

typedef struct
{
    ngx_str_t bingo;
    ngx_str_t path;
    ngx_str_t key;
    ngx_str_t words;
} ngx_http_bingo_config_conf_t;

ngx_command_t ngx_http_bingo_config_commands[] =
{
    {
        ngx_string("bingo_config"),
        NGX_HTTP_MAIN_CONF  | NGX_CONF_TAKE1,
        ngx_conf_set_bingo_config,
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_bingo_config_conf_t, bingo),
        NULL
    },
    {
        ngx_string("bingo_path"),
        NGX_HTTP_MAIN_CONF  | NGX_CONF_TAKE1,
        ngx_conf_set_bingo_path,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL
    },
     

    ngx_null_command
};


ngx_module_t ngx_http_bingo_config_module =
{
    NGX_MODULE_V1, /*对应到结构体下面的ctx_index、index、spare0、spare1、spare2、spare3、version变量不需要在定义时赋值，可以用Nginx准备好的宏NGX_MODULE_V1来定义，
它已经定义好了这7个值。#define NGX_MODULE_V1          0, 0, 0, 0, 0, 0, 1
*/

    &ngx_http_bingo_config_module_ctx, /*ctx用于指向一类模块的上下文结构体,在HTTP框架初始化时完成的*/

    ngx_http_bingo_config_commands, /**commands将处理nginx.conf中的配置项,有哪些指令可以处理 */
    NGX_HTTP_MODULE, /**模块类别,有五种值,NGX_HTTP_MODULE、NGX_CORE_MODULE、
    NGX_CONF_MODULE、NGX_EVENT_MODULE、NGX_MAIL_MODULE,我们现在例子是http */
    NULL,/*虽然从字面上理解应当在master进程启动时回调init_master，但到目前为止，框架代码从来不会调用它，因此，可将init_master设为NULL */
    NULL, /*init_module回调方法在初始化所有模块时被调用。在master/worker模式下，这个阶段将在启动worker子进程前完成*/
    NULL, /* init_process回调方法在正常服务前被调用。在master/worker模式下，多个worker子进程已经产生，在每个worker进程
    的初始化过程会调用所有模块的init_process函数*/
    NULL,/* 由于Nginx暂不支持多线程模式，所以init_thread在框架代码中没有被调用过，设为NULL*/
    NULL,// 同上，exit_thread也不支持，设为NULL
    NULL, /* exit_process回调方法在服务停止前调用。在master/worker模式下，worker进程会在退出前调用它，见ngx_worker_process_exit*/
    NULL, // exit_master回调方法将在master进程退出前被调用
    NGX_MODULE_V1_PADDING
};




static ngx_int_t   preconfiguration(ngx_conf_t *cf)
{
    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------preconfiguration");
    return NGX_OK;

}

static ngx_int_t   postconfiguration(ngx_conf_t *cf)
{
    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------postconfiguration");
    ngx_http_conf_ctx_t *http_root_conf = (ngx_http_conf_ctx_t *)ngx_get_conf(cf->cycle->conf_ctx, ngx_http_module);

    ngx_http_bingo_config_conf_t  *mycf;
    mycf = (ngx_http_bingo_config_conf_t *)http_root_conf->loc_conf[ngx_http_bingo_config_module.ctx_index];
    ngx_log_error(NGX_LOG_ALERT, cf->log, 0, "in http{} test_str=%V", &mycf->bingo);
    return NGX_OK;

}

/*static void       *create_main_conf(ngx_conf_t *cf){
	ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------create_main_conf");
		return NGX_CONF_OK;

}

static char       *init_main_conf(ngx_conf_t *cf, void *conf){
	ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------init_main_conf");
		return NGX_CONF_OK;

}

static void       *create_srv_conf(ngx_conf_t *cf){
	ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------create_srv_conf");
		return NGX_CONF_OK;

}

static char       *merge_srv_conf(ngx_conf_t *cf, void *prev, void *conf){
	ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------merge_srv_conf");
	return NGX_CONF_OK;
}*/


static void       *create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_bingo_config_conf_t *mycf;

    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------create_loc_conf");

    // 分配内存
    mycf = (ngx_http_bingo_config_conf_t *) ngx_pcalloc(cf->pool, sizeof(ngx_http_bingo_config_conf_t));

    if(mycf == NULL)
    {
        return NULL;
    }

    return mycf;
}


static ngx_int_t ngx_http_bingo_handler(ngx_http_request_t* r) {
	ngx_log_error(NGX_LOG_EMERG, r->connection->log,0,"ngx_http_bingo_handler is called %s!",r->captures_data);


    
	return 1;

	}

/*
static  char       *merge_loc_conf(ngx_conf_t *cf, void *prev, void *conf){
	ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------merge_loc_conf");
	return NGX_CONF_OK;
}*/

static char *ngx_conf_set_bingo_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------ngx_conf_set_bingo_config");

    char *p = conf;
    ngx_str_t *field, *value;
    //ngx_conf_post_t *post;

    field = (ngx_str_t *)(p + cmd->offset);


    value = cf->args->elts;
    *field = value[1];

    ngx_http_core_loc_conf_t* clcf;
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_bingo_handler;
 
    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------get config %V", field);

    return NGX_CONF_OK;
}

static char *ngx_conf_set_bingo_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------ngx_conf_set_bingo_path");
    ngx_str_t *content;

    char *p = conf;

    ngx_http_bingo_config_conf_t *bingo_conf;

    ngx_str_t  *value;
    bingo_conf = (ngx_http_bingo_config_conf_t *)(p + cmd->offset);

    value = cf->args->elts;
    bingo_conf->path = value[1];
    content = ngx_read_file_content(cf, &bingo_conf->path);
    bingo_conf->key.data=ngx_palloc(cf->pool,content->len);
    content->data=content->data+sizeof(PRE_KEY)-sizeof(u_char);
    content->len=content->len-sizeof(PRE_KEY);

    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------contnet:%V", content);

    if(ngx_decode_base64(&bingo_conf->key, content)==NGX_ERROR){
    	 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------error key, can't decode");

    }
    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------get path %V, get content: %V", &bingo_conf->path, &bingo_conf->key);
    
    ngx_str_t *in=ngx_palloc(cf->pool, sizeof(ngx_str_t));
    ngx_str_set(in,"M4TQtjkFWnaQGZLV2qFWHS68LhncUJB5G4aK90JYBi8=");
    ngx_str_t *dein=ngx_palloc(cf->pool, sizeof(ngx_str_t));
    
    dein->len=0;
    dein->data=ngx_pcalloc(cf->pool,in->len);

     if(ngx_decode_base64(dein, in)==NGX_ERROR){
         ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------error key, can't decode");

    }
    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------target: %V", dein);
     ngx_decode_aes_ceb(cf,dein->data,bingo_conf->key.data);


    return NGX_CONF_OK;

}

static u_char  *ngx_decode_aes_ceb(ngx_conf_t *cf,u_char *in, u_char *key){
    u_char *src,*p;
    AES_KEY aes;
    if(AES_set_decrypt_key(key, 128, &aes) < 0)
    {
        return 0;
    }
    int len=strlen((char *)in);
    src = ngx_palloc(cf->pool, (size_t)len);
    p=src;
     while(len!=0){
      AES_ecb_encrypt(in, src, &aes, AES_DECRYPT);
      in+=AES_BLOCK_SIZE;
      src+=AES_BLOCK_SIZE;
      len-=AES_BLOCK_SIZE;
     }
      ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------detarget: %s",p);

     return p;
}


static ngx_str_t  *ngx_read_file_content(ngx_conf_t *cf, ngx_str_t *path)
{
	ngx_str_t *value;
    u_char *content;
    ngx_file_t file;
    ngx_file_info_t fi;
    size_t size;
    ssize_t    n;

    if (ngx_conf_full_name(cf->cycle, path, 1) != NGX_OK)
    {

    }

    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------get full path %V", path);

    ngx_memzero(&file, sizeof(ngx_file_t));
    file.name = *path;
    file.log = cf->log;

    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------start open file", path);

    file.fd = ngx_open_file(path->data, NGX_FILE_RDONLY, 0, 0);
    if (file.fd == NGX_INVALID_FILE)
    {
        ngx_log_error(NGX_LOG_CRIT, cf->log, 0, "open file %s failed", path->data);
    }

    if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR)
    {
        ngx_log_error(NGX_LOG_CRIT, cf->log, 0, "open file %s failed", path->data);
    }

    size = (size_t) ngx_file_size(&fi);

    content = ngx_palloc(cf->pool, size);
    if (content == NULL)
    {
    	ngx_log_error(NGX_LOG_CRIT, cf->log, 0, "open file %s failed", path->data);
    }

    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "--------start read file:%s, size: %d", path->data,size);

    n = ngx_read_file(&file, content, size, 0);
    if (n == NGX_ERROR || (size_t)n != size)
    {
        ngx_log_error(NGX_LOG_CRIT, cf->log, 0, "read file %s failed", path->data);
    }

    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "-------- read file:%s, size: %d", content,n);
    value = ngx_palloc(cf->pool,sizeof(ngx_str_t));
    value->data=content;
    value->len=size;

     ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "222222");
    return value;
}
 

 